package com.naiterui.ehp.bp.support.utils;

import static com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS;

import cn.hutool.json.JSONUtil;
import java.io.IOException;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.TimeZone;

import org.springframework.core.ParameterizedTypeReference;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.naiterui.ehp.bp.support.exception.JsonException;

import lombok.extern.slf4j.Slf4j;

/**
 * 转换json工具类
 */

@SuppressWarnings("unused")
@Slf4j
public class JsonMapper extends ObjectMapper {

    private static final JsonMapper JSON_MAPPER = new JsonMapper();
    private static final long serialVersionUID = 8021069890967252247L;

    private JsonMapper() {
        super.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        //允许单引号
        super.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
        //允许出现特殊字符和转义符
        super.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
        // 设置输入时忽略在JSON字符串中存在但Java对象实际没有的属性
        this.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        super.enable(ALLOW_NUMERIC_LEADING_ZEROS);
        // 设置时区 getTimeZone("GMT+8:00")
        this.setTimeZone(TimeZone.getDefault());
        this.registerModules(new JavaTimeModule());
    }

    public static JsonMapper getJsonMapper() {
        return JSON_MAPPER;
    }

    public static String toJson(Object obj) throws JsonException {
        try {
            return JSON_MAPPER.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            log.error("json 转换异常 json:{}", obj, e);
            throw new JsonException(String.format("json转换异常 Class:%s", obj.getClass()), e);
        }
    }

    public static <T> T toObject(String json, ParameterizedTypeReference<T> responseTypeReference) throws JsonException {
        Type type = responseTypeReference.getType();
        JavaType javaType = JsonMapper.getJavaType(type);
        try {
            return JSON_MAPPER.readValue(json, javaType);
        } catch (IOException e) {
            log.error("json 转换异常 type:{} json:{}", type, json, e);
            throw new JsonException(String.format("json转换异常 json:%s", json), e);
        }
    }

    public static <T> T toObject(String json, Class<T> clazz) throws JsonException {
        try {
            return JSON_MAPPER.readValue(json, clazz);
        } catch (IOException e) {
            throw new JsonException(String.format("json转换异常 json:%s", json), e);
        }
    }

    public static <T> List<T> toList(String json, Class<T> clazz) throws JsonException {
        try {
            return JSON_MAPPER.readValue(json, JSON_MAPPER.getTypeFactory().constructCollectionType(ArrayList.class, clazz));
        } catch (IOException e) {
            throw new JsonException(String.format("json转换异常 json:%s clazz:%s", json, clazz.getName()), e);
        }
    }

    public static JsonNode toNode(String json) throws JsonException {
        try {
            return JSON_MAPPER.readTree(json);
        } catch (IOException e) {
            throw new JsonException(String.format("json转换异常 json:%s", json), e);
        }
    }

    public static <T> T toObject(String json, JavaType javaType) throws JsonException {
        try {
            return JSON_MAPPER.readValue(json, javaType);
        } catch (IOException e) {
            throw new JsonException(String.format("json转换异常 json:%s javaType:%s", json, javaType.getTypeName()), e);
        }
    }

    public static JavaType getJavaType(Type type) {
        TypeFactory typeFactory = JSON_MAPPER.getTypeFactory();
        return typeFactory.constructType(type);
    }

    public static boolean isJson(String str) {
        return JSONUtil.isJson(str);
    }

}
